#include "Editor.h"
#include "State.h"
#include "PointerState.h"
#include "Command.h"
#include "GeneralEdit.h"
#include "Basic/FileSystem.h"
#include "Basic/Stream.h"
#include "Material.h"
#include "CSG.h"
#include "Global.h"
namespace FinalRendering
{
	namespace Editor
	{
		using namespace GxRendering::UI;

		WorldEditorSingleton * WorldEditor;

		void InitEditor(Form * form, UIEntry * ui)
		{
			Engine::CreateGlobal(true);
			WorldEditor = new WorldEditorSingleton();
			WorldEditor->Init(form, ui);
			FinalRendering::Engine::MaterialFile::Init();
		}

		void FinalizeEditor()
		{
			FinalRendering::Engine::MaterialFile::Finalize();
			Scene::Finalize();
			WorldEditor->dlgCompile.Release();
			delete WorldEditor;
			Engine::DestroyGlobal();
		}

		EditorPreference::EditorPreference()
		{
			GridColor[0] = Color(80,80,80,255);
			GridColor[1] = Color(140, 100, 80, 255);
			GridColor[2] = Color(140, 180, 200, 255);
			MinScrGridSize = 8;
			PerspectiveViewMousePrecision = PI/500;
			wchar_t buffer[1024];
			GetCurrentDirectory(1024, buffer);
			WorkPath = buffer;
			CameraTurnAngle = PI;
			CameraMinSpeed = 64.0f;
			CameraMaxSpeed = 2048.0f;
			CameraAcceleration = 512.0f;
			LoadPreference();
		}

		EditorPreference::~EditorPreference()
		{
			SavePreference();
		}

		String EditorPreference::GetPreferenceFileName()
		{
			wchar_t ** cmdLine;
			int num;
			cmdLine = CommandLineToArgvW(GetCommandLine(),&num);
			String cPath = cmdLine[0];
			cPath = ExtractFilePath(cPath);
			LocalFree(cmdLine);
			cPath += L"UserOptions.upf";
			return cPath;
		}

		void EditorPreference::LoadPreference()
		{
			try
			{
				String cPath = GetPreferenceFileName();
				FileStream fs(cPath);
				WorkPath.LoadFromStream(&fs);
				fs.ReadBuffer(GridColor,3);
				fs.ReadBuffer(&MinScrGridSize,1);
				fs.ReadBuffer(&PerspectiveViewMousePrecision,1);
				fs.ReadBuffer(&CameraMinSpeed, 1);
				fs.ReadBuffer(&CameraMaxSpeed, 1);
				fs.ReadBuffer(&CameraAcceleration, 1);
				fs.ReadBuffer(&CameraTurnAngle, 1);
			}
			catch(...)
			{

			}
		}

		void EditorPreference::SavePreference()
		{
			String cPath = GetPreferenceFileName();
			try
			{
				FileStream fs(cPath, gxfmWrite);
				WorkPath.SaveToStream(&fs);
				fs.WriteBuffer(GridColor,3);
				fs.WriteBuffer(&MinScrGridSize,1);
				fs.WriteBuffer(&PerspectiveViewMousePrecision,1);
				fs.WriteBuffer(&CameraMinSpeed, 1);
				fs.WriteBuffer(&CameraMaxSpeed, 1);
				fs.WriteBuffer(&CameraAcceleration, 1);
				fs.WriteBuffer(&CameraTurnAngle, 1);
			}
			catch(...)
			{
			}
		}

		void WorldEditorSingleton::Init(Form * form, UIEntry * ui)
		{
			mainForm = form;
			file = new FileControl(form, ui);
			SelectMode = smGroup;
			cmdTail = 0;
			EditTexcoord = false;
			BrushType = btBox;
			BrushEdges = 3;
			SnapToGrid = true;
			NewDoc();
			Scene::Init();
		}

		Form * WorldEditorSingleton::GetMainForm()
		{
			return mainForm;
		}

		void WorldEditorSingleton::NewDoc()
		{
			StateStack.Clear();
			Scene = new Editor::Scene();
			Scene->OnGetMaterialPath.Bind(this, &WorldEditorSingleton::SceneGetMaterialPath);
			StateStack.Add(new PointerState());
			Selection.Clear();
		}

		void WorldEditorSingleton::SceneGetMaterialPath(String & path)
		{
			path = GetMaterialPath();
		}

		bool WorldEditorSingleton::Close()
		{
			return file->Close();
		}

		bool WorldEditorSingleton::Save()
		{
			return file->Save();
		}

		bool WorldEditorSingleton::Open()
		{
			return file->Open();
		}

		bool WorldEditorSingleton::SaveAs()
		{
			return file->SaveAs();
		}

		bool WorldEditorSingleton::New()
		{
			return file->New();
		}

		String WorldEditorSingleton::GetMaterialPath()
		{
			wchar_t buf[1024];
			String rs = Preference.WorkPath + L"./materials/";
			GetFullPathName(rs.GetStrPtr(), 1024, buf,0);
			rs = buf;
			MakeDirectory(rs);
			return rs;
		}

		void WorldEditorSingleton::Execute(Command * cmd)
		{
			cmd->PerformAction(Scene.operator ->());
			if (!cmd->Cancelled)
			{
				if (cmdTail < Commands.Count())
				{
					Commands.Delete(cmdTail, Commands.Count()-cmdTail);
				}
				Commands.Add(cmd);
				cmdTail = Commands.Count();
			}
			else
				delete cmd;
		}

		void WorldEditorSingleton::Undo()
		{
			if (CanUndo())
			{
				cmdTail --;
				Commands[cmdTail]->UndoAction(Scene.operator ->());
			}
			GetCurrentState();
		}

		void WorldEditorSingleton::Redo()
		{
			if (CanRedo())
			{
				Commands[cmdTail]->PerformAction(Scene.operator->());
				cmdTail ++;
			}
		}

		bool WorldEditorSingleton::CanUndo()
		{
			return (cmdTail > 0);
		}

		bool WorldEditorSingleton::CanRedo()
		{
			return (cmdTail < Commands.Count());
		}

		void WorldEditorSingleton::PushState(FinalRendering::Editor::State *state)
		{
			StateStack.Add(state);
		}

		void WorldEditorSingleton::PopState()
		{
			if (StateStack.Count())
			{
				StateStack[StateStack.Count()-1] = 0;
				StateStack.Delete(StateStack.Count()-1);
			}
		}

		State * WorldEditorSingleton::GetCurrentState()
		{
			if (StateStack.Count())
				return StateStack.End().operator->();
			else
				return 0;
		}

		void WorldEditorSingleton::SelectionChanged()
		{
			GetCurrentState()->UpdateSelection();
			OnSelectionChanged.Invoke();
		}

		void WorldEditorSingleton::Delete()
		{
			DeleteObjectCommand * cmd = new DeleteObjectCommand();
			for (int i=0; i<Scene->Brushes.Count(); i++)
			{
				if (Scene->Brushes[i]->Selected)
					cmd->brushID.Add(i);
			}
			for (int i=0; i<Scene->Entities.Count();i++)
			{
				if (Scene->Entities[i]->Selected)
				{
					cmd->entityID.Add(i);
				}
			}
			Execute(cmd);
			GetCurrentState()->UpdateSelection();
		}

		void WorldEditorSingleton::UpdateViews()
		{
			Views->UpdateAllViews();
		}

		void WorldEditorSingleton::Finalize()
		{
			Scene::Finalize();
			StateStack.Clear(true);
			Commands.Clear(true);
			Scene = 0;
		}

		void WorldEditorSingleton::CancelSelection()
		{
			Selection.Clear();
			for (int i=0;i<Scene->Brushes.Count(); i++)
			{
				for (int j=0; j<Scene->Brushes[i]->Polygons.Count(); j++)
					Scene->Brushes[i]->Polygons[j].Selected = false;
				Scene->Brushes[i]->Selected = false;
			}
			for (int i=0;i<Scene->Entities.Count(); i++)
			{
				Scene->Entities[i]->Selected = false;
			}
			GetCurrentState()->UpdateSelection();
		}

		void WorldEditorSingleton::SetState(FinalRendering::Editor::State *state)
		{
			if (StateStack.Count())
				StateStack.End() = state;
			else
				PushState(state);
			Views->UpdateAllViews();
		}

		void WorldEditorSingleton::Copy()
		{
			CopyObjectCommand * cmd = new CopyObjectCommand();
			cmd->selection.Add(Selection);
			Execute(cmd);
		}

		void WorldEditorSingleton::Paste()
		{
			PasteObjectCommand * cmd = new PasteObjectCommand();
			Execute(cmd);
		}

		void WorldEditorSingleton::Cut()
		{
			CompoundCommand * cmd = new CompoundCommand();
			CopyObjectCommand * ccmd = new CopyObjectCommand();
			ccmd->selection.Add(Selection);
			cmd->commands.Add(ccmd);
			DeleteObjectCommand * dcmd = new DeleteObjectCommand();
			for (int i=0; i<Scene->Brushes.Count(); i++)
			{
				if (Scene->Brushes[i]->Selected)
					dcmd->brushID.Add(i);
			}
			for (int i=0; i<Scene->Entities.Count();i++)
			{
				if (Scene->Entities[i]->Selected)
				{
					dcmd->entityID.Add(i);
				}
			}
			cmd->commands.Add(dcmd);
			Execute(cmd);
		}

		void WorldEditorSingleton::SelectAll()
		{
			for (int i=0; i<Scene->Brushes.Count(); i++)
			{
				Scene->Brushes[i]->Selected = true;
				if (EditTexcoord)
				{
					for (int j=0; j<Scene->Brushes[i]->Polygons.Count(); j++)
						Scene->Brushes[i]->Polygons[j].Selected = true;
				}
			}
			for (int i=0; i<Scene->Entities.Count(); i++)
				Scene->Entities[i]->Selected = true;
			GetCurrentState()->UpdateSelection();
		}

		void WorldEditorSingleton::TextureAdjust_TreatAsOne()
		{
			if (!EditTexcoord)
				return;
			if (!Selection.Count())
				return;

			TexcoordSystem sys = Scene->Brushes[Selection[0].ID]->Polygons[Selection[0].SecondaryID].TexcoordSystem;
			for (int i=1; i<Selection.Count(); i++)
			{
				int id = Selection[i].ID;
				int id2 = Selection[i].SecondaryID;
				Brush & b = *Scene->Brushes[id];
				Polygon & p = Scene->Brushes[id]->Polygons[id2];
				TexcoordSystem tsys = p.TexcoordSystem;
				p.TexcoordSystem = sys;
				b.RecalcPolygonTexturing(id2);
				p.TexcoordSystem = tsys;
				b.ReCalcPolygonTexcoordSystem();
				b.RecalcPolygonTexturing(id2);
			}

			UpdateViews();
		}

		void WorldEditorSingleton::SetBrushFlag(bool ignore)
		{
			class SetBrushFlagCommand : public Command
			{
			private:
				Array<Editor::Selection> selection;
				Array<bool> lstValue;
				bool ignore;
			public:
				virtual void PerformAction(Editor::Scene * scene)
				{
					for (int i=0; i<selection.Count(); i++)
					{
						if (selection[i].Type == Editor::Selection::otBrush)
						{
							lstValue.Add(scene->Brushes[selection[i].ID]->Polygons[selection[i].SecondaryID].Ignore);
							scene->Brushes[selection[i].ID]->Polygons[selection[i].SecondaryID].Ignore = ignore;
						}
					}
					WorldEditor->UpdateViews();
				}
				virtual void UndoAction(Editor::Scene * scene)
				{
					for (int i=0; i<selection.Count(); i++)
					{
						if (selection[i].Type == Editor::Selection::otBrush)
						{
							scene->Brushes[selection[i].ID]->Polygons[selection[i].SecondaryID].Ignore = lstValue[i];
						}
					}
					WorldEditor->UpdateViews();
				}
			public:
				SetBrushFlagCommand(const Array<Editor::Selection>& sel, bool _ignore)
					:ignore(_ignore)
				{
					selection = sel;
				}
			};

			if (Selection.Count())
			{
				Array<Editor::Selection> sel;
				for (int i=0; i<Selection.Count(); i++)
				{
					Editor::Selection s;
					if (Selection[i].Type != Editor::Selection::otBrush)
						continue;
					s.Type = Selection[i].Type;
					s.ID = Selection[i].ID;
					if (EditTexcoord)
					{
						s.SecondaryID = Selection[i].SecondaryID;
						sel.Add(s);
					}
					else
					{
						for (int j=0; j<Scene->Brushes[s.ID]->Polygons.Count(); j++)
						{
							s.SecondaryID = j;
							sel.Add(s);
						}
					}
				}
				if (sel.Count())
				{
					SetBrushFlagCommand * cmd = new SetBrushFlagCommand(sel, ignore);
					Execute(cmd);
				}
			}
		}

		void WorldEditorSingleton::Carve()
		{
			if (EditTexcoord)
				return;
			if (Selection.Count())
			{
				Array<int> bid;
				for (int i=0; i<Selection.Count(); i++)
				{
					if (Selection[i].Type == Selection::otBrush)
					{
						bid.Add(Selection[i].ID);
					}
				}
				if (bid.Count())
				{
					CarveCommand * cmd = new CarveCommand();
					cmd->brushes.Add(bid);
					Execute(cmd);
				}
			}
			UpdateViews();
		}

		void WorldEditorSingleton::MakeHollow()
		{
			if (EditTexcoord)
				return;
			if (Selection.Count())
			{
				Array<int> bid;
				for (int i=0; i<Selection.Count(); i++)
				{
					if (Selection[i].Type == Selection::otBrush)
					{
						bid.Add(Selection[i].ID);
					}
				}
				if (bid.Count())
				{
					MakeHollowCommand * cmd = new MakeHollowCommand();
					cmd->brushes.Add(bid);
					Execute(cmd);
				}
			}
		}

		void WorldEditorSingleton::Group()
		{
			if (EditTexcoord)
				return;
			if (Selection.Count())
			{
				GroupCommand * cmd = new GroupCommand();
				cmd->selection.Add(Selection);
				Execute(cmd);
			}
		}

		void WorldEditorSingleton::Ungroup()
		{
			if (EditTexcoord)
				return;
			if (Selection.Count())
			{
				UngroupCommand * cmd = new UngroupCommand();
				cmd->Selection.Add(Selection);
				Execute(cmd);
			}
		}

		void WorldEditorSingleton::ClearUnusedGroups()
		{
			for (int i=0; i<Scene->Groups.Count(); i++)
			{
				int gid = Scene->Groups[i]->ID;
				bool ref = false;
				for (int i=0; i<Scene->Brushes.Count(); i++)
				{
					if (Scene->Brushes[i]->GroupID == gid)
					{
						ref = true;
						break;
					}
				}
				if (!ref)
				{
					for (int i=0; i<Scene->Entities.Count(); i++)
					{
						if (Scene->Entities[i]->GroupID == gid)
						{
							ref = true;
							break;
						}
					}
				}
				if (!ref)
				{
					Scene->Groups.Delete(Scene->FindGroupID(gid));
					for (int j=0; j<Scene->Groups.Count(); j++)
					{
						if (Scene->Groups[j]->ParentID == gid)
							Scene->Groups[j]->ParentID = -1;
					}
				}
			}
		}

		void WorldEditorSingleton::ProcessLocalAccelerator(GxRendering::UI::UIKeyEventArgs &e)
		{
			switch (e.Key)
			{
			case VK_DELETE:
				Delete();
				break;
			case L'C':
				if (e.Shift == SS_CONTROL)
				{
					Copy();
				}
				else if (e.Shift == (SS_CONTROL|SS_SHIFT))
				{

				}
				break;
			case L'V':
				if (e.Shift == SS_CONTROL)
				{
					Paste();
				}
				break;
			case L'X':
				if (e.Shift == SS_CONTROL)
				{
					Cut();
				}
				break;
			case L'A':
				if (e.Shift == SS_CONTROL)
				{
					SelectAll();
				}
				break;
			case L'D':
				if (e.Shift == SS_CONTROL)
				{
					CancelSelection();
				}
				break;
			case L'Z':
				if (e.Shift == SS_CONTROL)
				{
					Undo();
				}
				break;
			case L'Y':
				if (e.Shift == SS_CONTROL)
				{
					Redo();
				}
				break;
			case L'G':
				if (e.Shift == SS_CONTROL)
				{
					Group();
				}
				break;
			case L'B':
				if (e.Shift == SS_CONTROL)
				{
					Ungroup();
				}
				break;
			}
			Views->UpdateAllViews();
		}

	}
}